import { FunctionPlugin, HyperFormula } from "hyperformula";
import { getCpasTableByName, getCpasTableCells, getDisclosureApiData, } from "../api/DesignTableApi";
import Dexie from "dexie";
import { CpasCacheDb } from "../../../tools/db";
import { getYears, getZcbmId, isNumber, setCurrentAuditYear, } from "../../../tools/Tools";
var ErrorType;
(function (ErrorType) {
    /** Division by zero. */
    ErrorType["DIV_BY_ZERO"] = "DIV_BY_ZERO";
    /** Unknown function name. */
    ErrorType["NAME"] = "NAME";
    ErrorType["VALUE"] = "VALUE";
    ErrorType["NUM"] = "NUM";
    ErrorType["NA"] = "NA";
    /** Cyclic dependency. */
    ErrorType["CYCLE"] = "CYCLE";
    /** Wrong address reference. */
    ErrorType["REF"] = "REF";
    /** Array spill error. */
    ErrorType["SPILL"] = "SPILL";
    /** Invalid/missing licence error. */
    ErrorType["LIC"] = "LIC";
    /** Generic error */
    ErrorType["ERROR"] = "ERROR";
})(ErrorType || (ErrorType = {}));
var ArgumentTypes;
(function (ArgumentTypes) {
    /**
     * String type.
     */
    ArgumentTypes["STRING"] = "STRING";
    /**
     * Floating point type.
     */
    ArgumentTypes["NUMBER"] = "NUMBER";
    /**
     * Boolean type.
     */
    ArgumentTypes["BOOLEAN"] = "BOOLEAN";
    /**
     * Any non-range value.
     */
    ArgumentTypes["SCALAR"] = "SCALAR";
    /**
     * Any non-range, no-error type.
     */
    ArgumentTypes["NOERROR"] = "NOERROR";
    /**
     * Range type.
     */
    ArgumentTypes["RANGE"] = "RANGE";
    /**
     * Integer type.
     */
    ArgumentTypes["INTEGER"] = "INTEGER";
    /**
     * String representing complex number.
     */
    ArgumentTypes["COMPLEX"] = "COMPLEX";
    /**
     * Range or scalar.
     */
    ArgumentTypes["ANY"] = "ANY";
})(ArgumentTypes || (ArgumentTypes = {}));
/**
 * 公式服务：用于单元格取数，勾稽校验的后台取数，运算支撑。
 *
 * 公式异步解决思路:
 *
 * 核心：
 *      1. 由于单元格异步公式不支持，必须要想办法将取数逻辑转换为同步。
 *      2. 第一次请求的自定义公式都是 ... , 检测如果是 ... 就不进行计算直接输出 ... 。
 *      3. 当异步数据都请求完成后，可以构建完整表时。进行统一表格重算
 *      4. 公式树的相计算依赖于hyper本身。
 *
 * 1. 是否可以拿到表中的所有自定义公式。 倾向于-> 1.3 逻辑方案

 1.1 遍历gridData数据，检测枚举的自定义公式。根据特征进行提取（表名/自定义公式)
 1.2 hyper是否提供相关的API，从构建的data中提取所有输入的自定义公式.
 1.3 通过hyper提供的自定义函数钩子，维护一个异步队列。 ->
 编写一个循环事件不断检测异步队列（例如300ms检测一次队列，如果没有待执行任务则。执行基于同步数据的公式重算）。->
 每次新增公式时也可以被检测到。

 * 2. 进行统一请求，做map映射。 或者检索公式中所有的表。以【表】/【单元格公式】为粒度做map缓存
 *
 2.1  遍历gridData数据，检测枚举的自定义公式。进行提取（表名) , 默认所有的自定义公式第一个参数为表名. 进行表名内存级别的缓存。
 2.2  遍历gridData数据，检测枚举的自定义公式。 进行自定义公式的整体提取（包含参数）。 例如:Cell("未审财务数据","货币资金","期初余额")作为key映射。进行缓存。

 问题：
 1. 表如果更新了，如何更新缓存中的数据 ？ -> 检测披露表每次更新时，进行树节点级别的时间戳更新标记。 每次重算记录一个时间戳进行节点对比。数据库表中增加更新时间字段。
 2. 表缓存效率和单元格缓存力度哪个更合适。考虑点： 执行效率和内存成本。-> 单元格缓存内存成本更低，但是不利于后续内存级别的数据统计。
 3. 缓存存放的位置?  localSorage(需要自己根据数据集进行运算)/indexdb（取数可以利用sql语句） (调用范围更广泛可以跨项目) / 内存cpasTable中维护（不可以跨项目）

 * 3. 所有公式统一请求完毕后，进行表格重算。
 *    问题:
 *    1. 重算需要整体表格的数据，后台进行重算的时候要依赖于hyperFormula. 进行builder数据。基于builder的数据进行整体后台重算。
 *
 *
 *    和厂哥总结：
 *
 *    1. 检测公式方式，采用自定义函数的回调钩子，将每个自定义公式表名提出，放到异步队列数组中。每300毫秒进行轮训异步任务执行取表数据
 *    2. 公式按照特征进行提取，提取出所有该表中用到的数据表。
 *    3. 按照表力度进行数据缓存，缓存在indexdb中。
 *    4. 当异步队列中检测不到可执行任务时进行数据重算。从indexdb中取数
 *    5. 缓存更新策略由于报表基础数据和斌哥那里针对于数据集无法做到表级别的更新时间戳。所以操作保持统一，用户手动点击重算进行缓存更新。
 *    6. 公式参数顺序 (table,key,column,....)
 *    7. 披露表没有年度字段，当多年时采用表名+年度的方式进行存储。例如: 固定资产-2017.
 *
 *
 *    公式相关接口:
 *
 *    1. 单元格取数同步 (❌) / 异步队列时间片检测方式 （✅） 已完成
 同步请求数据方案: 功能可行，实际效果不行。要等待http一个一个出去请求。还是要考虑利用indexdb，减少网络并发的返回。
 const result = syncLoad();
 return parseFloat(result.dValue);

 2. 目前单元格取数类型

 2.1 CpasTable单元格取数类型， 逻辑走 CP_CELL单元格取数公式。不需要传年度参数。 年度通过表名做体现如: 2017-固定资产

 公式暂定为:  CP_CELL(“固定资产”,”应收账款”,”期初余额-2017”)  ✅  📌：厂哥，丽莎确认

 2.2 斌哥后台接口数据集如: 底稿结构化数据,科目审定明细表,报告基础数据等...会通过后台接口的方式进行返回。
 接口为: /getDisclosureApiData
 参数为:
 {
 "name": "fa.getList",
 "dbName": "FI-2017-00001",
 "year": "2017"
 }
 返回格式为: ArrayList , 其中以"主键"中文做indexdb索引快速定位到行，定位到行之后直接用column取值。
 公式暂定为:    FS_CELL(“报告基础数据”,”工程款”,”期初审定数”,”2017”) 📌：厂哥，丽莎确认

 2.3 单元格公式年度表示法：  📌 厂哥，丽莎确认

 CpasTable Data
 CP_CELL(“固定资产”,”应收账款”,”期初余额-2017”)  //如果不加年度表示的是本年度，如果新的年度会在新的项目库构建

 彬哥数据集Data (结构化底稿、报表基础数据。。。。)
 FS_CELL(“报告基础数据”,”工程款”,”期初审定数”,”2017”)


 CP_CELL("T货币资金","库存现金","期初余额")
 CP_CELL("子级添加","123","金额"，2017)
 FS_CELL("fa.mxb","1003","原值期初未审","2017")
 IN_CELL("固定资产","期末余额")

 3. 单元格取数取不到或报错的，错误处理  -> 丽莎反馈，如果取不到或者为null都用0来计算 📌
 4. 后台单元格公式解析运算，需要。用于勾稽汇总运算。
 *
 *
 */
//["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
//CpasTable类型取数公式;
const CpasTableFormula = "CP_CELL";
//外部取数源，主要是斌哥那边的数据集。
const OutFormula = "FS_CELL";
//表内取数源
const IN_FORMULA = "IN_CELL";
export default class HyperFormulaService {
    static instance = null;
    db;
    constructor() {
        if (!HyperFormulaService.instance) {
            this.db = new CpasCacheDb();
            HyperFormulaService.instance = this;
            //启动异步公式runloop
            this.timeCallback();
        }
        else {
            return HyperFormulaService.instance;
        }
    }
    isNeedRecompute = false;
    //维护一个内存基本的异步队列。 每300毫秒检查一次该队列是否还有值。如果没有了则相当于异步任务都执行完成。 进行map列表映射构建。自定义公式中增加从map映射表中取值的逻
    static task = [];
    //TODO 迁移到electron中进行管理。二级map缓存。 披露表与财务数据建议单实例切换。
    static memoryMapCache = {};
    //注册需要更新的组件
    static registerGridRef = [];
    //队头计数处理
    headTaskStateWait = 0;
    intervalStop = false;
    parseAsyncIntervalStop = false;
    //关闭时间片检测状态
    setIntervalStop(flag) {
        if (!flag) {
            console.warn("时间片检测被关闭***************");
        }
        this.intervalStop = flag;
    }
    //清空indexDb缓存和内存缓存
    async clearCache() {
        //检测intervalStop是否被异常关闭，进行重启
        if (this.intervalStop) {
            console.warn("检测到intervalStop异常关闭，进行重启timeCallback策略");
            this.setIntervalStop(false);
            this.timeCallback();
        }
        HyperFormulaService.task = [];
        HyperFormulaService.memoryMapCache = {};
        setCurrentAuditYear(null);
        //TODO 需要验证是否会导致组件释放不了
        // HyperFormulaService.registerGridRef = [];
        const isOpen = this.db.isOpen();
        if (isOpen) {
            try {
                console.log("this.db isOpen 为 true 开始进行清理");
                await this.db.cpasTableData.clear();
                await this.db.cacheTable.clear();
                await this.db.disclosure.clear();
                console.log("isOpen 为 true 清理完毕");
            }
            catch (e) {
                console.error(e.message, e);
            }
        }
    }
    /**
     * 删除表结构
     */
    async deleteTableStructure() {
        await Dexie.delete("cpasCacheDb");
    }
    /**
     * Excel公式按照偏移量刷新公式
     * offset 偏移量: 正数为所有公式向右偏移offset , 负数为左右公式向左偏移offset
     * type row , column
     */
    static transformExcelGridData(data, offset, type) {
        if (data.length < 1) {
            console.error("未检测到数组长度");
            return null;
        }
        let transFormData = [...data];
        let resultData = [];
        let hyperData = null;
        if (type === "row") {
            if (offset >= 0) {
                hyperData = HyperFormula.buildFromArray(transFormData);
                hyperData.addRows(0, [0, offset]);
                resultData = hyperData.getAllSheetsSerialized()["Sheet1"];
                resultData.splice(0, offset);
            }
            else {
                const repeat = (arr, n) => Array.from({ length: arr.length * n }, (_, i) => arr[i % arr.length]);
                const defaultData = transFormData[0];
                offset = Math.abs(offset);
                for (let i = 0; i < offset; i++) {
                    const tmpData = repeat([0], defaultData.length);
                    transFormData.unshift(tmpData);
                }
                hyperData = HyperFormula.buildFromArray(transFormData);
                hyperData.removeRows(0, [0, offset]);
                resultData = hyperData.getAllSheetsSerialized()["Sheet1"];
            }
        }
        else {
            if (offset >= 0) {
                hyperData = HyperFormula.buildFromArray(transFormData);
                //加列
                hyperData.addColumns(0, [0, offset]);
                resultData = hyperData.getAllSheetsSerialized()["Sheet1"];
                resultData.map((item) => {
                    return item.splice(0, offset);
                });
            }
            else {
                transFormData.map((item) => {
                    return item.unshift(0);
                });
                hyperData = HyperFormula.buildFromArray(transFormData);
                //减列
                hyperData.removeColumns(0, [0, Math.abs(offset)]);
                resultData = hyperData.getAllSheetsSerialized()["Sheet1"];
            }
        }
        return resultData;
    }
    /**
     * 提取公式中的自定义公式
     * @param formula
     */
    static getHyperCustomFormula(formula) {
        const formula_reg = /(CP_CELL?.*?\(.+?\))|(FS_CELL:?.*?\(.+?\))|(IN_CELL:?.*?\(.+?\))/g;
        const result = formula.match(formula_reg);
        //提取参数
        //提取后的公式 const re = result[0]
        // re.substring(re.indexOf("(")+1).split(",")
        //重复公式去重
        return Array.from(new Set(result));
    }
    /**
     * 提取公式的参数
     */
    static getHyperCustomFormulaParams(formula) {
        const formula_params_start = formula.indexOf("(") + 1;
        const formula_params_end = formula.indexOf(")");
        return formula
            .substring(formula_params_start, formula_params_end)
            .split(",");
    }
    /**
     * 提取公式的函数名
     */
    static getHyperCustomFormulaName(formula) {
        return formula.substring(0, formula.indexOf("("));
    }
    /**
     * 根据task生成公式
     */
    static parseFormulaByTask(task) {
        const { type } = task;
        let formula = "";
        if (type === CpasTableFormula) {
            const { tname, state, rmap, cmap, data_id } = task;
            if (data_id) {
                formula = `${CpasTableFormula}(${tname},${rmap},${cmap},${data_id})`;
            }
            else {
                formula = `${CpasTableFormula}(${tname},${rmap},${cmap})`;
            }
        }
        else if (type === OutFormula) {
            const { tname, key, prop, data_id } = task;
            formula = `${OutFormula}(${tname},${key},${prop},${data_id})`;
        }
        return formula;
    }
    /**task错误处理方式 */
    taskErrorHandle(task, error) {
        const formula = HyperFormulaService.parseFormulaByTask(task);
        HyperFormulaService.memoryMapCache[formula] = 0;
        console.log(task);
        console.error(`----------------Cache:run loop:队头阻塞超过阀值强制弹出:${formula}------------------`);
        console.error(error);
        this.headTaskStateWait = 0;
    }
    destroy() {
        console.log("公式检测服务，检测到组件销毁；进行定时器销毁");
        try {
            this.parseAsyncIntervalStop = true;
            this.setIntervalStop(true);
            this.db.close();
        }
        catch (e) {
            console.warn(`HyperFormulaServer 销毁失败:${e}`);
        }
    }
    parseAsyncInterval = (fn, time) => {
        const timeout = () => {
            setTimeout(() => {
                fn();
                if (!this.parseAsyncIntervalStop) {
                    timeout();
                }
            }, time);
        };
        timeout();
    };
    myInterval = (fn, time) => {
        const timeout = () => {
            setTimeout(() => {
                fn();
                if (!this.intervalStop) {
                    timeout();
                }
            }, time);
        };
        timeout();
    };
    //runLoop 时间片检测
    timeCallback() {
        this.myInterval(async () => {
            const timestamp = Date.now();
            const isErrorHeadTask = this.headTaskStateWait > 20;
            if (isErrorHeadTask) {
                // console.error(
                //   `----------------Cache:run loop:时间片为${timestamp} 队头阻塞无法继续往下执行停止轮训检测:${HyperFormulaService.task.join()}------------------`
                // );
                // this.intervalStop = true;
                // return;
                const task = HyperFormulaService.task.pop();
                if (task) {
                    this.taskErrorHandle(task, "未知错误");
                }
            }
            if (HyperFormulaService.task.length === 0) {
                //isNeedRecompute === true 进行重算，重算后将 isNeedRecompute = false
                if (this.isNeedRecompute) {
                    // if (this.hyper) {
                    console.log(`----------------Cache:run loop:时间片为${timestamp} 当前内存公式值${HyperFormulaService.memoryMapCache}`);
                    console.log(`----------------Cache:run loop:时间片为${timestamp} 异步队列数据缓存完毕，开始执行重算`);
                    // //触发公式重算 -> e.g. 调用自定义函数cp_cell
                    // try {
                    //   this.hyper.rebuildAndRecalculate();
                    //   //表格重选渲染
                    //   this.hot.render();
                    //   //重算后，重触发校验公式
                    // } catch (e) {
                    //   console.error(e);
                    //   this.isNeedRecompute = false;
                    // }
                    // this.hot.validateCells();
                    setTimeout(() => {
                        //相关的注册表格进行重渲染
                        for (let i = 0; i < HyperFormulaService.registerGridRef.length; i++) {
                            const gridRef = HyperFormulaService.registerGridRef[i];
                            if (gridRef) {
                                try {
                                    gridRef.hyper.rebuildAndRecalculate();
                                    console.log("表格重新渲染");
                                    gridRef.hot.render();
                                }
                                catch (e) {
                                    throw e;
                                }
                            }
                        }
                    }, 300);
                    // }
                    this.isNeedRecompute = false;
                }
                // console.log(`run loop: ${Date.now()}`);
                return;
            }
            console.log(`----------------Cache:run loop:时间片为${timestamp} 当前任务队列:${HyperFormulaService.task.join()}------------------`);
            while (HyperFormulaService.task.length) {
                // 异步await 根据表名取回数据集, 并设置到indexdb中, 存之前判断一下indexdb中是否存在
                // 获取当前队头
                const currentTask = HyperFormulaService.task[HyperFormulaService.task.length - 1];
                if (currentTask.state === "wait") {
                    //如果异步队列中有上次未处理完的任务，则取消本次时间切片。进行等待
                    console.log(`----------------Cache:run loop:时间片为${timestamp} 当前队头任务未处理完成，退出该时间片:${currentTask.toString()}------------------`);
                    this.headTaskStateWait++;
                    break;
                }
                //取数据列表发生错误，直接退出该时间片
                if (currentTask.state === "error") {
                    console.error(`Cache:run loop: 请求该公式数据列表 发生错误，弹出该任务`);
                    const task = HyperFormulaService.task.pop();
                    if (task) {
                        this.taskErrorHandle(task, "请求该公式数据列表 发生错误，弹出该任务");
                    }
                    break;
                }
                currentTask.state = "wait";
                const { type, tname, data_id } = currentTask;
                const zcbfId = getZcbmId();
                if (!zcbfId) {
                    const errorTask = HyperFormulaService.task.pop();
                    if (errorTask) {
                        this.taskErrorHandle(errorTask, "未获取到zcbfid，退出该时间片");
                    }
                    break;
                }
                let cacheTable;
                if (type !== IN_FORMULA) {
                    const data_id_data = currentTask.data_id ? currentTask.data_id : "";
                    let dbname;
                    if (type === CpasTableFormula) {
                        let cpas_type = "fs";
                        const parseTName = tname.split("|");
                        //ds或者zl类型
                        if (parseTName.length > 1) {
                            const sjnd = isNumber(data_id)
                                ? data_id
                                : await getYears(data_id);
                            cpas_type = parseTName[1] === "前" ? "ds" : "zl";
                            //披露表缓存数据集名称 = 组成部分id + "-" + 表名 + "-" + 年度 + "-" + 数据类型
                            dbname = zcbfId + "-" + tname + "-" + sjnd + "-" + cpas_type;
                        }
                        else {
                            //fs类型
                            //披露表缓存数据集名称 = 组成部分id + "-" + 表名 + "-" + 数据类型
                            dbname = zcbfId + "-" + tname + "-" + cpas_type;
                        }
                    }
                    if (type === OutFormula) {
                        const sjnd = isNumber(data_id) ? data_id : await getYears(data_id);
                        //取数源数据集  组成部分id + "-" + 表名 + "-" + 年度
                        dbname = zcbfId + "-" + tname + "-" + sjnd;
                    }
                    try {
                        await this.db.checkDbReset();
                        cacheTable = await this.db.cacheTable
                            .where("[name+isCache]")
                            .equals([dbname, 1])
                            .first();
                    }
                    catch (e) {
                        console.log(`Cache:run loop:  检查本地cache请求出错，退出该时间片`);
                        console.log(e);
                        const errorTask = HyperFormulaService.task.pop();
                        if (errorTask) {
                            this.taskErrorHandle(errorTask, " 检查本地cache请求出错，退出该时间片");
                        }
                        break;
                    }
                }
                /**
                 * 识别各种自定义公式，定位到相关的缓存数据集逻辑
                 */
                const { rmap, cmap } = currentTask;
                let formulaState = "finish";
                let errorMsg = "";
                let cacheResult;
                switch (type) {
                    case CpasTableFormula:
                        try {
                            cacheResult = await this.cacheCpasData(tname, rmap, cmap, timestamp, data_id, cacheTable);
                        }
                        catch (e) {
                            formulaState = "error";
                            errorMsg = e.message;
                            break;
                        }
                        break;
                    case OutFormula:
                        const { key, prop } = currentTask;
                        try {
                            cacheResult = await this.cacheOutTableData(tname, key, prop, data_id, timestamp, cacheTable);
                        }
                        catch (e) {
                            formulaState = "error";
                            errorMsg = e.message;
                            break;
                        }
                        break;
                    case IN_FORMULA:
                        // 取数公式表内TODO
                        // this.cacheInTableData(rmap, cmap, timestamp);
                        break;
                    default:
                        break;
                }
                if (!cacheResult) {
                    formulaState = "error";
                }
                //消耗掉该任务，如果此时接口处理时间大于阀值500ms, 则会引发下次时间切片进入。重复处理该任务->加入任务执行状态，如果每次时间片重复任务时，遇到非ready则跳过。
                currentTask.state = formulaState;
                if (formulaState === "error") {
                    const errorTask = HyperFormulaService.task.pop();
                    if (errorTask) {
                        this.taskErrorHandle(errorTask, errorMsg);
                    }
                }
                else {
                    HyperFormulaService.task.pop();
                    this.headTaskStateWait = 0;
                    console.log(`End---------Cache:run loop:时间片为${timestamp} 消费${tname}并弹出--------`);
                }
            }
        }, 300);
    }
    /**
     * cpasTable类型的取数
     * @param tname
     * @param timestamp
     */
    async cacheCpasData(tname, rmap, cmap, timestamp, data_id, cacheTable) {
        //公式key
        let formula = "";
        if (data_id) {
            formula = `${CpasTableFormula}(${tname},${rmap},${cmap},${data_id})`;
        }
        else {
            formula = `${CpasTableFormula}(${tname},${rmap},${cmap})`;
        }
        const zcbfId = getZcbmId();
        if (!zcbfId) {
            throw new Error("未获取到组成部分id");
        }
        let sjnd;
        //转换真实年度
        try {
            if (data_id) {
                sjnd = isNumber(data_id) ? data_id : await getYears(data_id);
            }
        }
        catch (e) {
            throw new Error("未获取审计年度");
        }
        try {
            let tid;
            //如果一级缓存已经做过数据集缓存，则直接处理内存缓存
            if (!cacheTable) {
                console.log(`Cache:run loop:时间片为${timestamp} 未检测到${tname}表缓存，开始缓存数据集`);
                // const data_id_data = data_id ? getYears(data_id) : "";
                let type = "fs";
                const parseTName = tname.split("|");
                if (parseTName.length > 1) {
                    type = parseTName[1] === "前" ? "ds" : "zl";
                }
                let cpas_table;
                if (type === "fs") {
                    cpas_table = await getCpasTableByName(tname, sjnd, type);
                }
                else {
                    cpas_table = await getCpasTableByName(parseTName[0], sjnd, type);
                }
                tid = cpas_table.data ? cpas_table.data.tid : null;
                if (!tid) {
                    throw new Error(`未找到${tname}表的tid`);
                }
                let result;
                if (type === "fs") {
                    result = await getCpasTableCells(tname, data_id, type);
                }
                else {
                    result = await getCpasTableCells(parseTName[0], sjnd, type);
                }
                if (result.code === 200 && result.data.length > 0) {
                    //过滤result中的rmap===cmap的值
                    result.data = result.data.filter((item) => {
                        item["zcbfId"] = zcbfId;
                        item["dataId"] = sjnd;
                        return (
                        //1.如果cmap为key则表示是0列值,如果是config则表示是配置列需要过滤
                        //2.如果rmap 和 cmap 相等可能两者都为空，并且逻辑不同所以也过滤掉. rmap === cmap 为空时代表0行隐藏列
                        item.rmap !== item.cmap &&
                            item.cmap !== "key" &&
                            item.cmap !== "config");
                    });
                    //此处应该更改为事务
                    await this.db.cpasTableData.bulkAdd(result.data);
                    if (type === "fs") {
                        await this.db.cacheTable.add({
                            //披露表缓存数据集名称 = 组成部分id + "-" + 表名 + "-" + 年度 + "-" + 数据类型
                            tid,
                            name: zcbfId + "-" + tname + "-" + type,
                            isCache: 1,
                        });
                    }
                    else {
                        await this.db.cacheTable.add({
                            tid,
                            name: zcbfId + "-" + tname + "-" + sjnd + "-" + type,
                            isCache: 1,
                        });
                    }
                    console.log(`Cache:run loop:时间片为${timestamp} ${tname}成功写入缓存`);
                }
                else {
                    console.log(`Cache:run loop:未读取到该数据表，或者数据长度为0`);
                    HyperFormulaService.memoryMapCache[formula] = 0;
                    return false;
                }
            }
            else {
                console.log(`Cache:run loop:时间片为${timestamp} 检测到${tname}表缓存已存在`);
            }
            const cell_result = await this.db.cpasTableData
                .where("[tid+dataId+rmap+cmap+zcbfId]")
                .equals([cacheTable ? cacheTable.tid : tid, sjnd, rmap, cmap, zcbfId])
                .first();
            //如果从一级缓存中提取到值，进行二级缓存存储
            if (cell_result) {
                //有新的缓存数据公式更新，需要执行重算
                this.isNeedRecompute = true;
                HyperFormulaService.memoryMapCache[formula] = cell_result.dValue || 0; //如果取到记录，未取到cell_result.dValue则默认为0
            }
            else {
                //从现有的缓存表中没有取到值，说明表中不存在该值。直接按照0返回处理
                this.isNeedRecompute = true;
                HyperFormulaService.memoryMapCache[formula] = 0;
            }
        }
        catch (e) {
            console.log(e);
            console.log(`Cache:run loop:时间片为${timestamp} 请求网络数据出错或者写入缓存失败`);
            throw e;
        }
        return true;
    }
    /**
     * cpasTable类型的取数
     * @param tname
     * @param timestamp
     */
    async cacheOutTableData(tname, key, prop, data_id, timestamp, cacheTable) {
        const formula = `${OutFormula}(${tname},${key},${prop},${data_id})`;
        const zcbfId = getZcbmId();
        if (!zcbfId) {
            throw new Error("未获取到组成部分id");
        }
        let sjnd;
        //转换真实年度
        try {
            if (data_id) {
                sjnd = isNumber(data_id) ? data_id : await getYears(data_id);
            }
        }
        catch (e) {
            throw new Error("未获取到sjnd");
        }
        try {
            //如果一级缓存已经做过数据集缓存，则直接处理内存缓存
            if (!cacheTable) {
                console.log(`Cache:run loop:时间片为${timestamp} 未检测到${tname}表缓存，开始缓存数据集`);
                const result = await getDisclosureApiData(tname, sjnd);
                if (result.ok) {
                    const data = result.data.map((item) => {
                        return {
                            ...item,
                            dataId: parseInt(sjnd),
                            tname,
                            zcbfId,
                        };
                    });
                    let cacheDisclosureDataListName = zcbfId + "-" + tname + "-" + sjnd;
                    //此处应该更改为事务
                    await this.db.disclosure.bulkAdd(data);
                    await this.db.cacheTable.add({
                        tid: cacheDisclosureDataListName,
                        name: cacheDisclosureDataListName,
                        isCache: 1,
                    });
                    console.log(`Cache:run loop:时间片为${timestamp} ${tname}成功写入缓存`);
                }
                else {
                    console.log(`Cache:run loop:未读取到该数据表，或者数据长度为0`);
                    HyperFormulaService.memoryMapCache[formula] = 0;
                    return false;
                }
            }
            else {
                console.log(`Cache:run loop:时间片为${timestamp} 检测到${tname}表缓存已存在`);
            }
        }
        catch (e) {
            console.log(e);
            console.log(`Cache:run loop:时间片为${timestamp} 请求网络数据出错或者写入缓存失败`);
            throw e;
        }
        const cell_result = await this.db.disclosure
            .where("[主键+dataId+tname+zcbfId]")
            .equals([key, parseInt(sjnd), tname, zcbfId])
            .first();
        //如果从一级缓存中提取到值，进行二级缓存存储
        if (cell_result) {
            //有新的缓存数据公式更新，需要执行重算
            this.isNeedRecompute = true;
            HyperFormulaService.memoryMapCache[formula] = cell_result[prop] || 0;
        }
        else {
            //从现有的缓存表中没有取到值，说明表中不存在该值。直接按照0返回处理
            this.isNeedRecompute = true;
            HyperFormulaService.memoryMapCache[formula] = 0;
        }
        return true;
    }
}
export class CustomFormulaPlugin extends FunctionPlugin {
    /**
     * CpasTable类型单元格取数
     * @param ast
     * @param state
     */
    cp_cell(ast, state) {
        const nullDate = this.config.nullDate;
        const gridRef = nullDate.gridRef;
        return this.runFunction(ast.args, state, this.metadata(CpasTableFormula), (tname, rmap, cmap, data_id) => {
            //1. 先从二级缓存keyMap中进行检测是否存在 key = CP_CELL(tname,rmap,cmap)
            let formula = "";
            if (data_id) {
                formula = `${CpasTableFormula}(${tname},${rmap},${cmap},${data_id})`;
            }
            else {
                formula = `${CpasTableFormula}(${tname},${rmap},${cmap})`;
            }
            if (HyperFormulaService.memoryMapCache[formula] ||
                HyperFormulaService.memoryMapCache[formula] === 0) {
                return parseFloat(HyperFormulaService.memoryMapCache[formula]);
            }
            //2. 如果都不存在则添加到执行队列中
            console.log(`----------------Cache:提取到新的公式表名加入task队列:${tname}------------------`);
            //任务队列去重，如果队列中已存在该任务，则不添加。
            const found = HyperFormulaService.task.find((t) => {
                return t.rmap === rmap && t.cmap === cmap;
            });
            if (!found) {
                HyperFormulaService.task.unshift({
                    type: CpasTableFormula,
                    tname,
                    rmap,
                    cmap,
                    data_id,
                    state: "ready",
                    gridRef,
                });
            }
            else {
                console.log(`----------------Cache:该队列存在该任务${formula} -> 不入队------------------`);
            }
            //默认异步渲染值
            return 0;
        });
    }
    fs_cell(ast, state) {
        /**
         * 斌哥取数源，外部取数源
         */
        const nullDate = this.config.nullDate;
        const gridRef = nullDate.gridRef;
        return this.runFunction(ast.args, state, this.metadata(OutFormula), (tname, key, prop, data_id) => {
            console.log(gridRef);
            //支持自定义变量模式,支持动态年度模式{year}
            if (data_id.startsWith("{")) {
                return 0;
            }
            // ----------------   formulaOff clearTable -----------------------------
            const memoryClearTable = gridRef.memoryClearTable;
            if (memoryClearTable && gridRef.props.name !== "innerTable") {
                return 0;
            }
            const tableConfig = gridRef.tableConfig;
            // 这里是如果tableConfig为 formulaOff 或者  clearTable为 true的情况下.将不走时间片公式计算.直接从内存中记录的maps映射中读取值
            // 如果是 clearTable 状态的话, 重算会消除该状态
            // formulaOff 公式断开连接，只有人工点击才能取消formulaOff状态, 或者批量恢复公式.
            if (tableConfig &&
                (tableConfig["formulaOff"] === true ||
                    tableConfig["clearTable"] === true ||
                    gridRef.dataSource === "server") && //如果非 formulaOff 或者 clearTable 则判数据来源. 如果是 loadTableByServer的则也取dValue
                gridRef.props.name !== "innerTable") {
                const { row, col } = state.formulaAddress;
                let data = null;
                try {
                    data = gridRef.hot?.getSourceData();
                }
                catch (e) {
                    throw e;
                }
                if (!data) {
                    return 0;
                }
                const maps = gridRef.gridService.getMapsByCoords(data, { row, col });
                const value = gridRef.currentDataMaps[`${maps.rmap}|||${maps.cmap}`];
                if (isNumber(value)) {
                    return parseFloat(value) || 0;
                }
                else {
                    return value || "";
                }
            }
            // ----------------   formulaOff clearTable -----------------------------
            //1. 先从二级缓存keyMap中进行检测是否存在 key = CP_CELL(tname,rmap,cmap)
            const formula = `${OutFormula}(${tname},${key},${prop},${data_id})`;
            if (HyperFormulaService.memoryMapCache[formula] ||
                HyperFormulaService.memoryMapCache[formula] === 0) {
                return parseFloat(HyperFormulaService.memoryMapCache[formula]);
            }
            //2. 如果都不存在则添加到执行队列中
            console.log(`----------------Cache:提取到新的公式表名加入task队列:${tname}------------------`);
            //任务队列去重，如果队列中已存在该任务，则不添加。
            const found = HyperFormulaService.task.find((t) => {
                return t.key === key && t.prop === prop;
            });
            if (!found) {
                HyperFormulaService.task.unshift({
                    type: OutFormula,
                    tname,
                    key,
                    prop,
                    data_id,
                    state: "ready",
                    gridRef,
                });
            }
            else {
                console.log(`----------------Cache:该队列存在该任务${formula} -> 不入队------------------`);
            }
            return 0;
        });
    }
}
CustomFormulaPlugin.implementedFunctions = {
    [OutFormula]: {
        method: "fs_cell",
        isVolatile: true,
        parameters: [
            { argumentType: ArgumentTypes.STRING },
            { argumentType: ArgumentTypes.STRING },
            { argumentType: ArgumentTypes.STRING },
            { argumentType: ArgumentTypes.STRING }, //年度枚举
        ],
    },
    // let's define the function's ID as `GREET`
    [CpasTableFormula]: {
        method: "cp_cell",
        isVolatile: true,
        parameters: [
            { argumentType: ArgumentTypes.STRING },
            { argumentType: ArgumentTypes.STRING },
            { argumentType: ArgumentTypes.STRING },
            { argumentType: ArgumentTypes.STRING, optionalArg: true }, //年度枚举
        ],
    },
    // [IN_FORMULA]: {
    //   method: "in_cell", //CELL(行映射，列映射)
    //   isVolatile: true,
    //   doesNotNeedArgumentsToBeComputed: true,
    //   isDependentOnSheetStructureChange: true,
    //   arrayFunction: true,
    //   parameters: [
    //     { argumentType: ArgumentTypes.STRING }, //行映射
    //     { argumentType: ArgumentTypes.STRING }, //列映射
    //   ],
    // },
};
export const CustomFormulaPluginTranslations = {
    enGB: {
        [CpasTableFormula]: CpasTableFormula,
        [OutFormula]: OutFormula,
        [IN_FORMULA]: IN_FORMULA,
    },
    enUS: {
        [CpasTableFormula]: CpasTableFormula,
        [OutFormula]: OutFormula,
        [IN_FORMULA]: IN_FORMULA,
    },
    // repeat for all languages used in your system
};
